/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/mfc/PGLWnd.h>
#include <pgl/mfc/PGLDefaultEvent.h>


// CPGLWnd

IMPLEMENT_DYNAMIC(CPGLWnd, CWnd)

CPGLWnd::CPGLWnd(CPGLRegionPtr pRegion, DWORD dwFlags)
:m_uPointedRegion(0), m_pRegion(pRegion), m_GDIpHelper(&m_GDIpInterface, m_pRegion), 
m_dwFlags(dwFlags), m_bPostResize(false)
{
	SetEvent( CPGLEventPtr( new CPGLDefaultEvent) );
}

CPGLWnd::~CPGLWnd()
{	   
	m_pRegion.reset(); 
	ClearEvent();
};

BEGIN_MESSAGE_MAP(CPGLWnd, CWnd)
	//{{AFX_MSG_MAP(CPGLWnd)
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_CREATE()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



// Gestionnaires de messages CPGLWnd

void CPGLWnd::SetDirty()
{
	m_GDIpHelper.SetDirty();
	InvalidateRect(NULL,FALSE);
};


void CPGLWnd::SetTripleBuffering(bool on)
{
	m_GDIpHelper.SetTripleBuffering(on);
	InvalidateRect(NULL,FALSE);
}

void CPGLWnd::SetFrontDirty()
{
	m_GDIpHelper.SetFrontDirty();
	InvalidateRect(NULL,FALSE);
};

void CPGLWnd::SetCacheDirty()
{
	m_GDIpHelper.SetCacheDirty();
	InvalidateRect(NULL,FALSE);
};

void CPGLWnd::OnPaint() 
{	
	using namespace Gdiplus;

	CRect clientRect;
	// blitting...
	if (!GetUpdateRect(&clientRect))
		return;

try
{
	// Create and set an array of real numbers.
	CRect rect;
	CClientDC dc(this);
	GetClientRect(clientRect);
	if((clientRect.Width() <= 0) || (clientRect.Height() <= 0))
		return ; // nothing to do here....

	Graphics graphics(dc.m_hDC);

	if (IsResizeNeeded())
	{
		m_GDIpInterface.SetGraphics(&graphics);
		m_GDIpInterface.BeginPaint(dc.m_hDC);
			m_pRegion->RefreshView(m_GDIpInterface,true);
		m_GDIpInterface.EndPaint();
		ResetResizeState();
	}

	m_GDIpHelper.Draw(clientRect, dc.m_hDC, &graphics);

	if (m_pEvent)
		m_pEvent->Draw(graphics,clientRect);

}
catch(std::exception const& e)
{
    AfxMessageBox(CString("Execption: ")+e.what());
}

}

void CPGLWnd::SetSize(int cx, int cy)
{
	using namespace Gdiplus;

try
{
	if (cx && cy && m_pRegion)
	{
		//Getting real size of client...
		CRect rect;
		GetClientRect(rect);

		CClientDC dc(this);
		Graphics graphics(dc.m_hDC);

		rect.top = cy - rect.top;
		rect.bottom = cy - rect.bottom;
		m_GDIpInterface.SetGraphics(&graphics);
		m_GDIpInterface.BeginPaint(dc.m_hDC);
			m_GDIpInterface.SetMainBoundingBox(rect);
			m_pRegion->SetViewport(rect.left, rect.bottom , rect.Width(), abs(rect.Height()));
		m_GDIpInterface.EndPaint();

		SetDirty();
		PostResize();
	}

}
catch(std::exception const& e)
{
    AfxMessageBox(CString("Execption: ")+e.what());
}

};

void CPGLWnd::OnSize(UINT nType, int cx, int cy) 
{
	using namespace Gdiplus;

	CWnd::OnSize(nType, cx, cy);

	if (nType == SIZE_MINIMIZED || nType ==SIZE_MAXHIDE || nType ==SIZE_MAXSHOW)
		return;

	PGL_TRACE(_T("CPGLWnd: Resizing graph\n"));

	SetSize(cx,cy);
};

void CPGLWnd::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// keeping information
	m_mouse.RightBtnPressed(point.x,point.y);
	// stopping mouse
	m_mouse.Stop();


	if (m_pEvent)
		m_pEvent->OnRButtonDown(nFlags,point);

	CWnd::OnRButtonDown(nFlags, point);
}

void CPGLWnd::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if (!m_pRegion)
	{
		CWnd::OnLButtonDown(nFlags, point);
		return;
	}

	// keeping mouse
	SetCapture();

	// finding pointed region...
	CPGLPointD pglPoint( point.x, point.y);
	m_uPointedRegion = m_pRegion->FindPointedRegion( m_GDIpInterface, pglPoint);	

	// mouse is down
	m_mouse.LeftBtnPressed(point.x, point.y);

	// handling event
	if (m_pEvent)
	{
		m_pEvent->SetRegion(m_uPointedRegion);
		m_pEvent->OnLButtonDown(nFlags,point);
	}

	CWnd::OnLButtonDown(nFlags, point);
}

void CPGLWnd::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (!m_pRegion)
	{
		CWnd::OnLButtonUp(nFlags, point);
		return;
	}

	//Releasing mouse
	ReleaseCapture();

	// stop mouse...
	m_mouse.Stop();
	m_mouse.Move(point.x,point.y);

	// handling event
	if (m_pEvent)
	{
		m_pEvent->SetRegion(m_uPointedRegion);
		m_pEvent->OnLButtonUp(nFlags, point);
	}

	m_mouse.LeftBtnReleased();

	CWnd::OnLButtonUp(nFlags, point);
}

void CPGLWnd::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (!m_pRegion)
	{
		CWnd::OnMouseMove(nFlags, point);
		return;
	}

	int lastx,lasty;

	// no buttons down, finding region
	if (!m_mouse.IsLeftBtnDown(lastx,lasty) && !m_mouse.IsRightBtnDown(lastx,lasty))
	{
		CPGLPointD pglPoint( point.x, point.y);
		m_uPointedRegion = m_pRegion->FindPointedRegion( m_GDIpInterface, pglPoint);	
	}


	if (m_mouse.IsLeftBtnDown(lastx,lasty))
		m_mouse.LeftMove();
	if (m_mouse.IsRightBtnDown(lastx,lasty))
		m_mouse.RightMove();

	// handling event
	if (m_pEvent)
	{
		m_pEvent->SetRegion(m_uPointedRegion);
		m_pEvent->OnMouseMove(nFlags,point);
	}

	// update mouse
	m_mouse.Move(point.x,point.y);
	
	CWnd::OnMouseMove(nFlags, point);
}


void CPGLWnd::OnDestroy() 
{
	PGL_TRACE(_T("CPGLWnd: Destroying window\n"));
	CWnd::OnDestroy();

	m_GDIpHelper.Destroy();

	m_pRegion.reset();
	ClearEvent();

}

BOOL CPGLWnd::OnEraseBkgnd( CDC* pDC )
{
    if (m_pRegion)	
        return TRUE;
    else
        return CWnd::OnEraseBkgnd(pDC);
}

int CPGLWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
	// enable triple buffering
	if (m_dwFlags & OptionTripleBuffering)
	{
		m_GDIpHelper.SetTripleBuffering(true);
	}


	if (m_dwFlags && OptionClipboardMetafile)
		m_eClipboardType = CPGLArchive::ClipBoardMetafile;
	else
		m_eClipboardType = CPGLArchive::ClipBoardBitmap;

	if (!m_pRegion)
		return -1;

try
{
	PostResize();

	if (m_pRegion && !(m_dwFlags & OptionNoZoomAll))
	{
		CClientDC dc(this);
		Gdiplus::Graphics graphics(dc.m_hDC);
		m_GDIpInterface.SetGraphics(&graphics);
		m_GDIpInterface.BeginPaint(dc.m_hDC);
			m_pRegion->ZoomAll(m_GDIpInterface,true);
		m_GDIpInterface.EndPaint();	
	}
}
catch(std::exception const& e)
{
    AfxMessageBox(CString("Execption: ")+e.what());
}


	return 0;
}


void CPGLWnd::SetEvent( CPGLEventPtr pEvent)
{
	ClearEvent();

	m_pEvent = pEvent;

	if( !m_pEvent)
		return;

	m_pEvent->SetParent(this);
	m_pEvent->SetGfx( &m_GDIpInterface);
	m_pEvent->SetRegion(m_uPointedRegion);
	m_pEvent->SetMouse(&m_mouse);
	m_pEvent->SetHelper(&m_GDIpHelper);
}

void CPGLWnd::ClearEvent()
{
	m_pEvent.reset();
}

LRESULT CPGLWndHooker::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    CPGLWnd* wnd = (CPGLWnd*)m_pOriginalWnd;
    CPoint point;
    POINTS p;
    p=MAKEPOINTS(lParam); 
    point.x = p.x;
    point.y=p.y;

    switch(nMsg)
    {
    case WM_MOUSEMOVE:
        wnd->OnMouseMove(wParam,point);
        break;                    
    case WM_RBUTTONDOWN:
        wnd->OnRButtonDown(wParam,point);
        break;        
    }

    return Default();
}
